using System;
using System.Reflection;
using Crainiate.Data.Persistence.Providers;
using Crainiate.Data.Persistence.Cache;

namespace Crainiate.Data.Persistence
{
    [Serializable]
	public abstract class Table: IPersistable
	{
		//Property variables
        [NonSerialized] Command _command;
		private Concurrency _concurrency;
        private bool _withInheritance;

    	#region Interface

		//Constructors
		public Table()
		{
            WithInheritance = true;
		}

        public Table(Command command)
        {
            Command = command;
            WithInheritance = true;
        }

        //Properties
		public virtual Command Command
		{
			get
			{
				return _command;
			}
            set
            {
                _command = value;
            }
		}

		public virtual Concurrency Concurrency
		{
			get
			{
				return _concurrency;
			}
            set
            {
                _concurrency = value;
            }
		}

		//Methods
		public virtual void Update()
		{
            if (Command == null) throw new TableException("The object could not be updated. Set the Command before attempting to update this object.");
    		Command.Update(this);
		}

		public virtual void Select()
		{
            if (Command == null) throw new TableException("The object could not be selected. Set the Command before attempting to select this object.");
            if (Concurrency == null) Concurrency = Command.Context.CreateConcurrency();
            Command.Select(this);

            LinkReferenceProperties(this.GetType(), this);
		}

		public virtual void Insert()
		{
            if (Command== null) throw new TableException("The object could not be inserted. Set the Command before attempting to insert for this object.");
            
            if (WithInheritance)
            {
                Command.Insert(this);
            }
            else
            {
                Command.Promote(this);
            }
		}

		public virtual void Delete()
		{
            if (Command == null) throw new TableException("The object could not be deleted. Set the Command before attempting to delete this object.");

            if (WithInheritance)
            {
                Command.Delete(this);
            }
            else
            {
                Command.Demote(this);
            }
		}

		public virtual bool Exists()
		{
            if (Command == null) throw new TableException("The object could not be checked. Set the Command before attempting to check this object.");
			return Command.Exists(this);
		}

        public virtual bool WithInheritance
        {
            get
            {
                return _withInheritance;
            }
            set
            {
                _withInheritance = value;
            }
        }

        //Link the context and command for the reference properties
        //for the type passed in for this table object
        private void LinkReferenceProperties(Type type, IPersistable value)
        {
            ReflectionCacheItem cache = Component.Instance.ReflectionCache.Lookup(type);

            if (cache.HasObjects)
            {
                foreach (PropertyInfo prop in cache.ReferenceProperties)
                {
                    if (typeof(Table).IsAssignableFrom(prop.PropertyType))
                    {
                        Table table = prop.GetValue(value, null) as Table;

                        if (table != null)
                        {
                            table.Command = this.Command;
                            LinkReferenceProperties(prop.PropertyType, table);
                        }
                    }
                }
            }

            //Recurse for sub classes
            if (!cache.IsUnderlying) LinkReferenceProperties(cache.BaseType, value);
        }

		#endregion
	}
}
